package
{
    /* ------------------------------------------------------------------ */
    /* MathContext -- Math context settings */
    /* ------------------------------------------------------------------ */
    /* Copyright IBM Corporation, 1997, 2000, 2005. All Rights Reserved. */
    /* */
    /* The MathContext object encapsulates the settings used by the */
    /* BigDecimal class; it could also be used by other arithmetics. */
    /* ------------------------------------------------------------------ */
    /* Notes: */
    /* */
    /* 1. The properties are checked for validity on construction, so */
    /* the BigDecimal class may assume that they are correct. */
    /* ------------------------------------------------------------------ */
    /* Author: Mike Cowlishaw */
    /* 1997.09.03 Initial version (edited from netrexx.lang.RexxSet) */
    /* 1997.09.12 Add lostDigits property */
    /* 1998.05.02 Make the class immutable and final; drop set methods */
    /* 1998.06.05 Add Round (rounding modes) property */
    /* 1998.06.25 Rename from DecimalContext; allow digits=0 */
    /* 1998.10.12 change to com.ibm.icu.math package */
    /* 1999.02.06 add javadoc comments */
    /* 1999.03.05 simplify; changes from discussion with J. Bloch */
    /* 1999.03.13 1.00 release to IBM Centre for Java Technology */
    /* 1999.07.10 1.04 flag serialization unused */
    /* 2000.01.01 1.06 copyright update */
    /* ------------------------------------------------------------------ */

    /**
     * The <code>MathContext</code> immutable class encapsulates the
     * settings understood by the operator methods of the {@link BigDecimal}
     * class (and potentially other classes). Operator methods are those
     * that effect an operation on a number or a pair of numbers.
     * <p>
     * The settings, which are not base-dependent, comprise:
     * <ol>
     * <li><code>digits</code>:
     * the number of digits (precision) to be used for an operation
     * <li><code>form</code>:
     * the form of any exponent that results from the operation
     * <li><code>lostDigits</code>:
     * whether checking for lost digits is enabled
     * <li><code>roundingMode</code>:
     * the algorithm to be used for rounding.
     * </ol>
     * <p>
     * When provided, a <code>MathContext</code> object supplies the
     * settings for an operation directly.
     * <p>
     * When <code>MathContext.DEFAULT</code> is provided for a
     * <code>MathContext</code> parameter then the default settings are used
     * (<code>9, SCIENTIFIC, false, ROUND_HALF_UP</code>).
     * <p>
     * In the <code>BigDecimal</code> class, all methods which accept a
     * <code>MathContext</code> object defaults) also have a version of the
     * method which does not accept a MathContext parameter. These versions
     * carry out unlimited precision fixed point arithmetic (as though the
     * settings were (<code>0, PLAIN, false, ROUND_HALF_UP</code>).
     * <p>
     * The instance variables are shared with default access (so they are
     * directly accessible to the <code>BigDecimal</code> class), but must
     * never be changed.
     * <p>
     * The rounding mode constants have the same names and values as the
     * constants of the same name in <code>java.math.BigDecimal</code>, to
     * maintain compatibility with earlier versions of
     * <code>BigDecimal</code>.
     *
     * @see BigDecimal
     * @author Mike Cowlishaw
     * @stable ICU 2.0
     */

    public class MathContext {
  
        /* ----- Properties ----- */
        /* properties public constant */
        /**
         * Plain (fixed point) notation, without any exponent.
         * Used as a setting to control the form of the result of a
         * <code>BigDecimal</code> operation.
         * A zero result in plain form may have a decimal part of one or
         * more zeros.
         *
         * @see #ENGINEERING
         * @see #SCIENTIFIC
         * @stable ICU 2.0
         */
        public static const NOTATION_PLAIN:int = 0; // [no exponent]
        
        /**
         * Standard floating point notation (with scientific exponential
         * format, where there is one digit before any decimal point).
         * Used as a setting to control the form of the result of a
         * <code>BigDecimal</code> operation.
         * A zero result in plain form may have a decimal part of one or
         * more zeros.
         *
         * @see #ENGINEERING
         * @see #PLAIN
         * @stable ICU 2.0
         */
        public static const NOTATION_SCIENTIFIC:int = 1; // 1 digit before .
        
        /**
         * Standard floating point notation (with engineering exponential
         * format, where the power of ten is a multiple of 3).
         * Used as a setting to control the form of the result of a
         * <code>BigDecimal</code> operation.
         * A zero result in plain form may have a decimal part of one or
         * more zeros.
         *
         * @see #PLAIN
         * @see #SCIENTIFIC
         * @stable ICU 2.0
         */
        public static const NOTATION_ENGINEERING:int = 2; // 1-3 digits before .
        
        // The rounding modes match the original BigDecimal class values
        /**
         * Rounding mode to round to a more positive number.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If any of the discarded digits are non-zero then the result
         * should be rounded towards the next more positive digit.
         * @stable ICU 2.0
         */
        public static const ROUND_CEILING:int = 2;
        
        /**
         * Rounding mode to round towards zero.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * All discarded digits are ignored (truncated). The result is
         * neither incremented nor decremented.
         * @stable ICU 2.0
         */
        public static const ROUND_DOWN:int = 1;
        
        /**
         * Rounding mode to round to a more negative number.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If any of the discarded digits are non-zero then the result
         * should be rounded towards the next more negative digit.
         * @stable ICU 2.0
         */
        public static const ROUND_FLOOR:int = 3;
        
        /**
         * Rounding mode to round to nearest neighbor, where an equidistant
         * value is rounded down.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If the discarded digits represent greater than half (0.5 times)
         * the value of a one in the next position then the result should be
         * rounded up (away from zero). Otherwise the discarded digits are
         * ignored.
         * @stable ICU 2.0
         */
        public static const ROUND_HALF_DOWN:int = 5;
        
        /**
         * Rounding mode to round to nearest neighbor, where an equidistant
         * value is rounded to the nearest even neighbor.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If the discarded digits represent greater than half (0.5 times)
         * the value of a one in the next position then the result should be
         * rounded up (away from zero). If they represent less than half,
         * then the result should be rounded down.
         * <p>
         * Otherwise (they represent exactly half) the result is rounded
         * down if its rightmost digit is even, or rounded up if its
         * rightmost digit is odd (to make an even digit).
         * @stable ICU 2.0
         */
        public static const ROUND_HALF_EVEN:int = 6;
        
        /**
         * Rounding mode to round to nearest neighbor, where an equidistant
         * value is rounded up.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If the discarded digits represent greater than or equal to half
         * (0.5 times) the value of a one in the next position then the result
         * should be rounded up (away from zero). Otherwise the discarded
         * digits are ignored.
         * @stable ICU 2.0
         */
        public static const ROUND_HALF_UP:int = 4;
        
        /**
         * Rounding mode to assert that no rounding is necessary.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * Rounding (potential loss of information) is not permitted.
         * If any of the discarded digits are non-zero then an
         * <code>ArithmeticException</code> should be thrown.
         * @stable ICU 2.0
         */
        public static const ROUND_UNNECESSARY:int = 7;
        
        /**
         * Rounding mode to round away from zero.
         * Used as a setting to control the rounding mode used during a
         * <code>BigDecimal</code> operation.
         * <p>
         * If any of the discarded digits are non-zero then the result will
         * be rounded up (away from zero).
         * @stable ICU 2.0
         */
        public static const ROUND_UP:int = 0;
        
        
        /* properties shared */
        /**
         * The number of digits (precision) to be used for an operation.
         * A value of 0 indicates that unlimited precision (as many digits
         * as are required) will be used.
         * <p>
         * The {@link BigDecimal} operator methods use this value to
         * determine the precision of results.
         * Note that leading zeros (in the integer part of a number) are
         * never significant.
         * <p>
         * <code>digits</code> will always be non-negative.
         *
         * @serial
         */
        internal var digits:int;
        
        /**
         * The form of results from an operation.
         * <p>
         * The {@link BigDecimal} operator methods use this value to
         * determine the form of results, in particular whether and how
         * exponential notation should be used.
         *
         * @see #ENGINEERING
         * @see #PLAIN
         * @see #SCIENTIFIC
         * @serial
         */
        internal var form:int; // values for this must fit in a byte
        
        /**
         * Controls whether lost digits checking is enabled for an
         * operation.
         * Set to <code>true</code> to enable checking, or
         * to <code>false</code> to disable checking.
         * <p>
         * When enabled, the {@link BigDecimal} operator methods check
         * the precision of their operand or operands, and throw an
         * <code>ArithmeticException</code> if an operand is more precise
         * than the digits setting (that is, digits would be lost).
         * When disabled, operands are rounded to the specified digits.
         *
         * @serial
         */
        internal var lostDigits:Boolean;
        
        /**
         * The rounding algorithm to be used for an operation.
         * <p>
         * The {@link BigDecimal} operator methods use this value to
         * determine the algorithm to be used when non-zero digits have to
         * be discarded in order to reduce the precision of a result.
         * The value must be one of the public constants whose name starts
         * with <code>ROUND_</code>.
         *
         * @see #ROUND_CEILING
         * @see #ROUND_DOWN
         * @see #ROUND_FLOOR
         * @see #ROUND_HALF_DOWN
         * @see #ROUND_HALF_EVEN
         * @see #ROUND_HALF_UP
         * @see #ROUND_UNNECESSARY
         * @see #ROUND_UP
         * @serial
         */
        internal var roundingMode:int;
        
        /* properties private constant */
        // default settings
        private static const DEFAULT_FORM:int = NOTATION_SCIENTIFIC;
        private static const DEFAULT_DIGITS:int = 9;
        private static const DEFAULT_LOSTDIGITS:Boolean = false;
        private static const DEFAULT_ROUNDINGMODE:int = ROUND_HALF_UP;
          
        /* properties private constant */
          
        private static const MIN_DIGITS:int = 0; // smallest value for DIGITS.
        private static const MAX_DIGITS:int = 999999999; // largest value for DIGITS. If increased,

        // the BigDecimal class may need update.
        // list of valid rounding mode values, most common two first
        private static const ROUNDS:Array = [ROUND_HALF_UP,ROUND_UNNECESSARY,ROUND_CEILING,ROUND_DOWN,ROUND_FLOOR,ROUND_HALF_DOWN,ROUND_HALF_EVEN,ROUND_UP];
          
          
        private static const ROUNDWORDS:Array = ["ROUND_HALF_UP","ROUND_UNNECESSARY","ROUND_CEILING","ROUND_DOWN","ROUND_FLOOR","ROUND_HALF_DOWN","ROUND_HALF_EVEN","ROUND_UP"]; // matching names of the ROUNDS values
  
        /* properties public constant */
        /**
         * A <code>MathContext</code> object initialized to the default
         * settings for general-purpose arithmetic. That is,
         * <code>digits=9 form=SCIENTIFIC lostDigits=false
         * roundingMode=ROUND_HALF_UP</code>.
         *
         * @see #SCIENTIFIC
         * @see #ROUND_HALF_UP
         * @stable ICU 2.0
         */
        public static const DEFAULT:MathContext = new MathContext(DEFAULT_DIGITS,DEFAULT_FORM,DEFAULT_LOSTDIGITS,DEFAULT_ROUNDINGMODE);
  
        public static const PLAIN:MathContext = new MathContext(0,NOTATION_PLAIN); // context for plain unlimited math

        /* ----- Constructors ----- */
        
        /**
         * Constructs a new <code>MathContext</code> with a specified
         * precision, form, lostDigits, and roundingMode setting.
         *
         * An <code>IllegalArgumentException</code> is thrown if the
         * <code>setdigits</code> parameter is out of range
         * (&lt;0 or &gt;999999999), or if the value given for the
         * <code>setform</code> or <code>setroundingmode</code> parameters is
         * not one of the appropriate constants.
         *
         * @param setdigits The <code>int</code> digits setting
         * for this <code>MathContext</code>.
         * @param setform The <code>int</code> form setting
         * for this <code>MathContext</code>.
         * @param setlostdigits The <code>boolean</code> lostDigits
         * setting for this <code>MathContext</code>.
         * @param setroundingmode The <code>int</code> roundingMode setting
         * for this <code>MathContext</code>.
         * @throws IllegalArgumentException parameter out of range.
         * @stable ICU 2.0
         */
        
        public function MathContext(setdigits:int, setform:int = DEFAULT_FORM, setlostdigits:Boolean = DEFAULT_LOSTDIGITS, setroundingmode:int = DEFAULT_ROUNDINGMODE) {
            // set values, after checking
            if (setdigits!=DEFAULT_DIGITS) {
                if (setdigits<MIN_DIGITS) {
                    throw new Error("Digits too small:"+" "+setdigits);
                }
                if (setdigits>MAX_DIGITS) {
                    throw new Error("Digits too large:"+" "+setdigits);
                }
            }

            {/*select*/
                if (setform==NOTATION_SCIENTIFIC) {
                    // [most common]
                } else if (setform==NOTATION_ENGINEERING) {
                } else if (setform==NOTATION_PLAIN) {
                } else {
                    throw new Error("Bad form value:"+" "+setform);
                }
            }
            
            if ((!(isValidRound(setroundingmode)))) {
                throw new Error("Bad roundingMode value:"+" "+setroundingmode);
            }
            
            digits=setdigits;
            form=setform;
            lostDigits=setlostdigits; // [no bad value possible]
            roundingMode=setroundingmode;
        }

        /**
         * Returns the digits setting.
         * This value is always non-negative.
         *
         * @return an <code>int</code> which is the value of the digits
         * setting
         * @stable ICU 2.0
         */
        public function getDigits():int {
            return digits;
        }
        
        /**
         * Returns the form setting.
         * This will be one of
         * {@link #ENGINEERING},
         * {@link #PLAIN}, or
         * {@link #SCIENTIFIC}.
         *
         * @return an <code>int</code> which is the value of the form setting
         * @stable ICU 2.0
         */
        public function getForm():int {
            return form;
        }
        
        /**
         * Returns the lostDigits setting.
         * This will be either <code>true</code> (enabled) or
         * <code>false</code> (disabled).
         *
         * @return a <code>boolean</code> which is the value of the lostDigits
         * setting
         * @stable ICU 2.0
         */
        public function getLostDigits():Boolean {
            return lostDigits;
        }
        
        /**
         * Returns the roundingMode setting.
         * This will be one of
         * {@link #ROUND_CEILING},
         * {@link #ROUND_DOWN},
         * {@link #ROUND_FLOOR},
         * {@link #ROUND_HALF_DOWN},
         * {@link #ROUND_HALF_EVEN},
         * {@link #ROUND_HALF_UP},
         * {@link #ROUND_UNNECESSARY}, or
         * {@link #ROUND_UP}.
         *
         * @return an <code>int</code> which is the value of the roundingMode
         * setting
         * @stable ICU 2.0
         */
        public function getRoundingMode():int {
            return roundingMode;
        }
        
        /** Returns the <code>MathContext</code> as a readable string.
         * The <code>String</code> returned represents the settings of the
         * <code>MathContext</code> object as four blank-delimited words
         * separated by a single blank and with no leading or trailing blanks,
         * as follows:
         * <ol>
         * <li>
         * <code>digits=</code>, immediately followed by
         * the value of the digits setting as a numeric word.
         * <li>
         * <code>form=</code>, immediately followed by
         * the value of the form setting as an uppercase word
         * (one of <code>SCIENTIFIC</code>, <code>PLAIN</code>, or
         * <code>ENGINEERING</code>).
         * <li>
         * <code>lostDigits=</code>, immediately followed by
         * the value of the lostDigits setting
         * (<code>1</code> if enabled, <code>0</code> if disabled).
         * <li>
         * <code>roundingMode=</code>, immediately followed by
         * the value of the roundingMode setting as a word.
         * This word will be the same as the name of the corresponding public
         * constant.
         * </ol>
         * <p>
         * For example:
         * <br><code>
         * digits=9 form=SCIENTIFIC lostDigits=0 roundingMode=ROUND_HALF_UP
         * </code>
         * <p>
         * Additional words may be appended to the result of
         * <code>toString</code> in the future if more properties are added
         * to the class.
         *
         * @return a <code>String</code> representing the context settings.
         * @stable ICU 2.0
         */
        public function toString():String {
            var formstr:String = null;
            var r:int = 0;
            
            var roundword:String = null;
            
            {/*select*/
                if (form==NOTATION_SCIENTIFIC) {
                    formstr="SCIENTIFIC";
                } else if (form==NOTATION_ENGINEERING) {
                    formstr="ENGINEERING";
                } else{
                    formstr="PLAIN";/* form=PLAIN */
                }
            }
            
            {
            
                var one:int = ROUNDS.length;
                r = 0;
                
                for(; one > 0; one--, r++){
                    if (roundingMode==ROUNDS[r]) {
                        roundword=ROUNDWORDS[r];
                        break;
                    }
                }
            }/*r*/
            
            return "digits="+digits+" "+"form="+formstr+" "+"lostDigits="+(lostDigits?"1":"0")+" "+"roundingMode="+roundword;
        }
        
        
        /* <sgml> Test whether round is valid. </sgml> */
        // This could be made shared for use by BigDecimal for setScale.
        
        private static function isValidRound(testround:int):Boolean {
            var r:int = 0;
            
            {
                var two:int = ROUNDS.length;
                r=0;
                for(; two > 0; two--,r++){
                    if (testround==ROUNDS[r]) {
                        return true;
                    }
                }
            }/*r*/
            
            return false;
        }
    }
}